/***********************************************************************
 * $Id : ast_main.cpp    2011/08/01 00:00:00
 *
 *       swhors@naver.com
 ***********************************************************************/

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <iostream>

#include <vector>
#include <algorithm>
#include <pthread.h>

#include <pthread.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>

#include <pwd.h>
#include <grp.h>

using namespace std;

#include "constant.h"

#include "skylistener.h"
#include "ast_util.h"
#include "ast_db.h"
#include "ast_event.h"
#include "acd_svr.h"
#include "acd_manager.h"
#include "ast_main.h"
#include "SkyConf.h"
#include "SkyConfUtil.h"

CSkyConf gSkyConf;

int  gRunProcess                      = 0;
int  gConnected                       = 0;
char gRecordPathName[MAX_PATH_LENGTH] = {0,};

// to asterisk : ami connection socket fd;
int gAmiCliSockFd = 0;

// listen from acd client : acd connection socket fd;
int gAcdSvrSockFd = 0;

// online db connection state
int gIsConnected2DB=0;

extern pthread_t gAcdManagerHd;

// 

RECVMSG gRecvMsg[MAX_RECV_BUFF_SIZE];

int sendCommand( char *fmt,...)
{
    int sRetCode = 0;
#ifdef HAVE_ASTERISK_H
    char sSendBuf[1024];
    memset( sSendBuf, 0, 1024 );
    va_list ap;
    va_start( ap, fmt );
    vsnprintf( sSendBuf, 1024, fmt, ap );
    va_end( ap );
    if( gAmiCliSockFd > 0 )
    {
        sRetCode = ( send( gAmiCliSockFd, sSendBuf, 1024,0 ) > 0 ? 1 : 0 );
    }
    else
    {
        log_message( (char *)"ERR : sendCommand Fail [Socket handler is fault.].\n" );
    }
#endif /* HAVE_ASTERISK_H */
    return sRetCode;
}

int stop_ami_server()
{
    gRunProcess = 0;

    log_message( (char *)"INFO : skylistener requested to stop[0].\n" );

    if( gAmiCliSockFd> 0 )
    {
        close( gAmiCliSockFd );
    }

    online_sql_disconnect( );

    Sleep(2000 );

    log_message( (char *)"INFO : skylistener requested to stop[1].\n" );
    return 1;
}

int create_ami_client()
{
    struct sockaddr_in serv_addr;
    struct hostent *server;

    gAmiCliSockFd = socket( AF_INET, SOCK_STREAM, 0 );
    if( gAmiCliSockFd < 0 )
    {
        log_message( (char *)"ERROR : %s [sock error : socket]\n", __FUNCTION__ );
        return 0;
    }

    server = gethostbyname( connect_addr );

    if( server == NULL)
    {
        log_message( (char *)"ERROR : %s [sock error : gethostbyname]\n", __FUNCTION__ );
        gRunProcess = 0;
        close( gAmiCliSockFd );
        return 0;
    }

    bzero( (char *) &serv_addr, sizeof( serv_addr) );
    serv_addr.sin_family = AF_INET;

    bcopy( (char *)server->h_addr,
            (char *)&serv_addr.sin_addr.s_addr,
            server->h_length );

    serv_addr.sin_port = htons( connect_port );
    if( connect( gAmiCliSockFd,
            (struct sockaddr *) &serv_addr,
            sizeof( serv_addr ) ) < 0)
    {
        log_message( (char *)"ERROR : %s [sock error : connect]\n", __FUNCTION__ );
        gRunProcess = 0;
        close( gAmiCliSockFd );
        return 0;
    }
    return 1;
}

/*
 * Create ACD Server
 * @pram [in] void
 *
 * @return -- void 
 */
int create_acd_server()
{
    struct sockaddr_in serv_addr;

    char sCallOutPath[MAX_PATH_LENGTH]  = {0,};
    char sCallListPath[MAX_PATH_LENGTH] = {0,};
    char sAstUserBuf[USERNAME_MAX_LEN]  = {0,};
    int  sWaitTime                      = 0;

    memset( sCallOutPath,  0, sizeof( char ) * MAX_PATH_LENGTH  );
    memset( sCallListPath, 0, sizeof( char ) * MAX_PATH_LENGTH  );
    memset( sAstUserBuf,   0, sizeof( char ) * USERNAME_MAX_LEN );

    gAcdSvrSockFd = socket( AF_INET, SOCK_STREAM, 0 );

    if( gAcdSvrSockFd < 0)
    {
        log_message( (char *)"ERROR : %s [sock error : socket]\n", __FUNCTION__ );
        return 0;
    }
    else
    {
        (void)GetConfCallListPath( sCallListPath, MAX_PATH_LENGTH  );
        (void)GetConfCallOutPath(  sCallOutPath,  MAX_PATH_LENGTH  );
        (void)GetConfAstUser(      sAstUserBuf,   USERNAME_MAX_LEN );

        sWaitTime = GetConfWaitTime( );

        int result = init_acd_manager( sCallOutPath,
                sCallListPath,
                sAstUserBuf,
                sWaitTime /* wait time of counsellor */
        );
        if( result <0 )
        {
            close( gAcdSvrSockFd );
            return 0;
        }
        else
        {
            int val = 1;
            if( setsockopt( gAcdSvrSockFd,
                    SOL_SOCKET,
                    SO_REUSEADDR,
                    (void *)&val,
                    sizeof( int ) ) == -1 )
            {
                log_message( (char *)"ERROR : %s [setsockopt]\n", __FUNCTION__ );
                close( gAcdSvrSockFd );
                return 0;
            }
            else
            {
                bzero( (char *) &serv_addr, sizeof( serv_addr ) );
                serv_addr.sin_family = AF_INET;
                serv_addr.sin_port = htons( listen_port );
                serv_addr.sin_addr.s_addr = INADDR_ANY;

                if( bind( gAcdSvrSockFd,
                        (struct sockaddr *) &serv_addr,
                        sizeof( serv_addr ) ) < 0 )
                {
                    log_message( (char *)"ERROR : %s [sock error : bind]\n", __FUNCTION__ );
                    close( gAcdSvrSockFd );
                    return 0;
                }
                else
                {
                    if( listen( gAcdSvrSockFd, 5) )
                    {
                        log_message( (char *)"ERROR : %s [sock error : listen]\n",
                                __FUNCTION__ );
                        close( gAcdSvrSockFd );
                        return 0;
                    }
                    else
                    {
                        /* success */
                        /* do nothing */
                    }
                }
            }
        }
    }

    return 1;
}

int run_ami_server( int thread_mode )
{
    fd_set           sReadFd;

    struct timeval   sTimeVal;
    char             sReadBuff[1024] = {0,};
    int              sReadCnt     = 0;
    int              sWriteCnt    = 0;
    int              sResult = 0;
    int              sCnt = 0;
    RECVMSG        * sRcvMsg = new RECVMSG( );
    REQUESTSOCK    * sReqSock = new REQUESTSOCK( );


    if( thread_mode )
    {
        /* do nothing, for future support */
    }
    else
    {
        /* do nothing, for future support */
    }

    log_message( (char *)"INFO : skylistener running..[1]\n" );

    gSkyConf.LoadConf( (char *)SKYCONF_FILE );

    // initialization

    memset( gRecordPathName, 0, sizeof( char ) * MAX_PATH_LENGTH );
    memset( sReadBuff, 0, sizeof( char ) * 1024 );

    (void)GetConfRecordPath( sReadBuff , 1024 );


    snprintf( gRecordPathName,
        MAX_PATH_LENGTH,
        "%s",
        ( strlen( sReadBuff ) > 0 ? sReadBuff : DEF_RECORD_PATH ) );

    memset( sReadBuff, 0, sizeof( char ) * 1024 );


    for( ; sCnt < MAX_RECV_BUFF_SIZE; sCnt++ )
    {
        memset( gRecvMsg[sCnt].mMsg, 0, 512 );
        gRecvMsg[sCnt].mMsgLen = 0;
    }

    // connect to online odbc.
    if( !online_sql_connect() )
    {
        log_message( (char *)"ERROR : %s [error : online_sql_connect]\n", __FUNCTION__ );
        return 0;
    }

#ifdef HAVE_ASTERISK_H
    // connect to ami server.
    if( !create_ami_client())
    {
        gAmiCliSockFd = 0;
        online_sql_disconnect( );
        log_message( (char *)"ERROR : %s [error : create_ami_client]\n",
            __FUNCTION__ );
        return 0;
    }
#else // HAVE_ASTERISK_H
    log_message( (char *)"INFO : This server does not have asterisk.!!!!!\n" );
#endif // HAVE_ASTERISK_H

    // create acd server socker.
    if( !create_acd_server() )
    {
        close( gAmiCliSockFd );
        online_sql_disconnect( );
        log_message( (char *)"ERROR : %s [error : create_acd_server]\n",
                     __FUNCTION__ );
        return 0;
    }

    gIsConnected2DB  = 1;
    gRunProcess     = 1;

    sTimeVal.tv_sec       = 0;
    sTimeVal.tv_usec      = 100000;

    FD_ZERO( &sReadFd );

    log_message( (char *)"INFO : skylistener running..[2]\n" );

    while( gRunProcess )
    {
        FD_SET( gAmiCliSockFd, &sReadFd );
        FD_SET( gAcdSvrSockFd, &sReadFd );

        sResult = select( gAcdSvrSockFd+1,
                          &sReadFd,
                          NULL,
                          NULL,
                          &sTimeVal );

        if( sResult == -1 )
        {
            log_message( (char *)"ERROR : %s [select].\n", __FUNCTION__ );
            break;
        }
        else
        {
            if( sResult > 0 )
            {
                // from acd client.
                if( FD_ISSET( gAcdSvrSockFd,&sReadFd ) == 1 )
                {
                    log_message( (char *)"INFO : Request from acd client.\n" );

                    sReqSock = new REQUESTSOCK( );
                    if( sReqSock )
                    {
                        socklen_t slen = (socklen_t)sizeof( struct sockaddr );
                        sReqSock->mAcptSockfd = accept( gAcdSvrSockFd,
                                (struct sockaddr *)&sReqSock->mCliAddr,
                                &slen );
                        if( sReqSock->mAcptSockfd > 0 )
                        {
                            // acpt_process;
                            if( pthread_create( &sReqSock->mThreadId,
                                    NULL,
                                    acdsvr_process,
                                    sReqSock ) != 0 )
                            {
                                log_message( (char *)"ERROR : %s "\
                                             "[pthread_create_acdsvr]\n",
                                             __FUNCTION__ );
                                delete sReqSock;
                            }
                        }
                        else
                        {
                            log_message( (char *)"ERROR : %s [accept_acdsvr]\n",
                                         __FUNCTION__ );
                            delete sReqSock;
                        }
                    }
                    else
                    {
                        log_message( (char *)"ERROR : %s [read_amicli]\n",
                                     __FUNCTION__ );
                    }
                }

                // from asterisk.
                if( FD_ISSET( gAmiCliSockFd, &sReadFd ) == 1 )
                {
                    bzero( sReadBuff, 1024 );
                    sReadCnt = read( gAmiCliSockFd,
                            sReadBuff,
                            1024 );
                    if( sReadCnt < 0 )
                    {
                        log_message( (char *)"ERROR : %s [read_amicli]\n",
                                __FUNCTION__ );
                        break;
                    }
                    else
                    {
                        sRcvMsg = new RECVMSG( );

                        memset( sRcvMsg->mMsg, 0, 1024 );
                        memcpy( sRcvMsg->mMsg, sReadBuff, sReadCnt );
                        sRcvMsg->mMsgLen = sReadCnt;

                        if( pthread_create( &sRcvMsg->mThreadId,
                                                   NULL,
                                                   msg_process,
                                                   sRcvMsg )
                            != 0 )
                        {
                            log_message( (char *)"ERROR : %s "\
                                         "[pthread_create_acdsvr]\n",
                                         __FUNCTION__ );
                            delete sRcvMsg;
                        }
                    }
                }
            }
        }
    }
    log_message( (char *)"INFO : skylistener ended..[0]\n" );
    Sleep( 200 );
    close( gAmiCliSockFd );
    gAmiCliSockFd = 0;
    online_sql_disconnect( );
    log_message( (char *)"INFO : skylistener ended..[1]\n" );
    return 0;
}

/////////////////////////////////////////////////////////////////////
